#coding: utf-8
"""
    file name: ArithmeticTest.py
    platform: Windows,Linux
    description: 100以内的加减法运算, 语音输入输出

    module author: Guangwei Jiang (Guangwei_Jiang@wistron.com)
    Create Date: 2020/9/6

"""
import random
import time
import win32com.client
import csv
import os
import speech_recognition as sr
from aip import AipSpeech
from pyaudio import PyAudio,paInt16
import requests,json,wave,time,pyttsx3
import re
import pandas as pd 

testCount = 60
maxValue = 100
isAddEnable = True
isSubEnable = True
isMulEnable = True
firstValueList = []
secondValueList = []
operatorList = [] # 0 for addition; 1 for subtraction
resultList = []
shuffleList = []
recordingFile = "recording.wav"
csvFilePath = "data"
summaryCsvFileName = os.path.join(csvFilePath, "summary.csv")
detailCsvFileName = os.path.join(csvFilePath, "detail.csv")

inputKeypad = "Keypad"
inputSpeech = "Speech"

#百度AI，配置信息
APP_ID = '22628473'#百度ai应用id
API_KEY = 'jOenHOG8WzfModCO9U73eDFH'#百度ai应用的键值
SECRET_KEY = 'gw0NBWImqAo1OTupxEr6yh1Odp64n2yv'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

def initArithmeticArray():
    """Function to generate firstValueList/secondValueList/operatorList/resultList

    |  Args -- NA

    |  Return -- NA

    >>> # generate firstValueList/secondValueList/operatorList/resultList
    >>> initArithmeticArray()
    """
    try:
        
        i = 0
        while i<testCount:
            #print("step1: " + str(i))
            if isAddEnable and i<testCount: 
                operatorList.append(0)
                i = i + 1
            if isSubEnable and i<testCount: 
                operatorList.append(1)
                i = i + 1
            if isMulEnable and i<testCount: 
                operatorList.append(2)
                i = i + 1
        
        i = 0
        while i < testCount:
            #print("step2: " + str(i))
            op1 = random.randint(1, maxValue)
            op2 = random.randint(1, maxValue)
            result = -1
            
            if operatorList[i] == 0:
                result = op1 + op2;
            if operatorList[i] == 1:
                result = op1 - op2;
            if operatorList[i] == 2:
                result = op1 * op2;
                if op1>6 or op2>6: # will through it away temporily
                    continue
            
            # skip the repeat item
            for j in range(i):
                if op1==firstValueList[j] and op2==secondValueList[j] and result==resultList[j]:
                    continue
            
            if result >= 0 and result <= maxValue:
                i = i + 1
                firstValueList.append(op1)
                secondValueList.append(op2)
                resultList.append(result)
                
        #print(firstValueList)
        #print(secondValueList)
        #print(operatorList)
        #print(resultList)
    except Exception as e:
        print(e)
    
def genShuffleList():    
    """Function to generate shuffleList so that can dis-order the list

    |  Args -- NA

    |  Return -- NA

    >>> # generate shuffleList
    >>> genShuffleList()
    """
    try:
        for i in range(testCount):
            shuffleList.append(i)
        #print(shuffleList)
    
        for i in range(testCount):
            randSwap = random.randint(0, testCount-1)
            temp = shuffleList[i]
            shuffleList[i] = shuffleList[randSwap]
            shuffleList[randSwap] = temp
        #print(shuffleList)
    except Exception as e:
        print(e)

def getFileContent(filePath):
    """Function to read file content

    |  Args:
    |  1. filePath: the path of the file need to read

    |  Return -- The content of the file

    >>> # read the file content
    >>> content = getFileContent("test.wav")
    """
    try:
        with open(filePath, 'rb') as fp:
            return fp.read()
    except Exception as e:
        print(e)
        return None


def getVoice(rate=16000):
    """Function to recod the voice and save to local computer

    |  Args:
    |  1. rate: the rate of recording audio

    |  Return -- NA

    >>> # record the voice
    >>> getVoice()
    """
    try:        
        r = sr.Recognizer()
        with sr.Microphone(sample_rate=rate) as source:
            print("开始录音")
            audio = r.listen(source)#读入声音信息
 
        with open(recordingFile, "wb") as f:
            f.write(audio.get_wav_data())#得到wave信息写入音频文件
        print("录音完成！")
        
    except Exception as e:
        print(e)

def getAnswerFromVoice():
    """Function to get Text from Speech by using baidu Speech-To-Text service. 

    |  Args -- NA

    |  Return: The digit number.

    >>> # Get the answer from the voice
    >>> result = getAnswerFromVoice()
    """
    result = -1
    cn2Digital = ['零', 0, '一', 1, '二', 2, '三', 3, '四', 4, '五', 5, '六', 6, '七', 7, '吧', 8, '八', 8, '九', 9, '十', 10]
    try:
        while(result==-1):
            getVoice()
            strInput = client.asr(getFileContent(recordingFile), 'wav', 16000, {'dev_pid': 1537})  # 识别本地文件
            strInput = strInput['result'][0]
            if len(strInput)>0:
                strInput = strInput[:-1] # 移除标点符号        
            print(strInput)
            lastChar = ''
            if len(strInput)>0:
                lastChar = strInput[-1]
            if lastChar in cn2Digital:
                for i in range(len(cn2Digital)):
                    if (lastChar==cn2Digital[i]):
                        result = cn2Digital[i+1]
                        break
            elif (strInput.isdigit()):
                result = int(strInput)
            else:
                digitList = re.findall(r"\d+\.?\d*",strInput)
                print(digitList)
                if len(digitList)>0:
                    result = int(digitList[-1])
            
        print(str(result))
        return result
    except Exception as e:
        print(e)
        return result

def getAnswerFromConsole():
    result = -1
    try:
        while(result == -1):
            strInput = input("请输入结果： ")
            if (strInput.isdigit()):
                result = int(strInput)
            else:
                print("输入字符非法，请重新输入。")
    except Exception as e:
        print(e)
    finally:
        return result

def arithmeticTest(op1, op2, operator, inputMethod):
    """Function to calculate for single time

    |  Args:
    |  1. op1: operands 1
    |  2. op2: operands 2
    |  3. operator: operator (0 for addition; 1 for subtraction)
    |  4. inputMethod: support Keypad/Speech, will support more in future

    |  Return -- True or False

    >>> # calculate
    >>> arithmeticTest(1, 2, 0)
    """
    testResult = False
    try:
        strVoice = ""
        if operator==0:
            strOp = '+'
            result = op1 + op2
            strVoice = str(op1) + "加" + str(op2)
        elif operator==1:
            strOp = '-'
            result = op1 - op2
            strVoice = str(op1) + "减" + str(op2)
        elif operator==2:
            strOp = '*'
            result = op1 * op2
            strVoice = str(op1) + "成以" + str(op2)
        strPrompt = "\r\n" + str(op1) + ' ' + strOp + ' ' + str(op2) + ' = '
        #print(strPrompt, end='')
        spk = win32com.client.Dispatch("SAPI.SpVoice")
        spk.Speak(strVoice)
        start = time.time()
        if inputMethod==inputKeypad:
            intInput = getAnswerFromConsole()
        elif inputMethod==inputSpeech:
            intInput = getAnswerFromVoice()
        duration = time.time() - start
        if (intInput == result):
            strResult = "对"
            print(strResult)
            spk.Speak(strResult)
            testResult = True
        else:
            strResult = "错"
            print(strResult)
            spk.Speak(strResult)
            testResult = False
            
        status = "Pass" if testResult else "Fail"
        saveToDetailCsv(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), str(op1)+strOp+str(op2), str(result), str(intInput), status, int(duration), inputMethod)
    except Exception as e:
        print(e)
    finally:
        return testResult

def startTest(inputMethod): 
    """Function to start the test

    |  Args:
    |  1. inputMethod: support keypad and speech, will support more in future

    |  Return -- right and wrong number

    >>> # start the test
    >>> right, wrong = startTest()
    """
    try:        
        promptSound(inputMethod)
        initArithmeticArray()
        genShuffleList()
        
        right = 0
        wrong = 0
        start = time.time()
        for i in range(testCount):
            index = shuffleList[i]
            status = arithmeticTest(firstValueList[index], secondValueList[index], operatorList[index], inputMethod)
            if status:
                right = right + 1
            else:
                wrong = wrong + 1
                
        duration = time.time() - start
        saveToSummaryCsv(right, wrong, int(duration), inputMethod)
        
        strResult = "做对了" + str(right) + "道题，" + "做错了" + str(wrong) + "道题！\n加油哦！"
        print(strResult)        
        spk = win32com.client.Dispatch("SAPI.SpVoice")
        spk.Speak(strResult)
        
        minute = duration/60
        second = duration%60
        strResult = "总共用时" + str(int(minute)) + "分钟 " + str(int(second)) + "秒"
        print(strResult)
        spk.Speak(strResult)
    except Exception as e:
        print(e)
    finally:
        return right, wrong
        
def saveToSummaryCsv(right, wrong, duration, inputMethod):
    """Function to save data to CSV file

    |  Args
    |  1. right: the number of right answer
    |  2. wrong: the number of wrong answer
    |  3. duration: the time cost during test
    |  4. inputMethod: support keypad/speech, will support more in future

    |  Return -- NA

    >>> # save the data to CSV file
    >>> saveToSummaryCsv(right, wrong, duration, inputMethod)
    """
    try:
        if not os.path.exists(summaryCsvFileName):            
            headers = ["Time", "Total", "Correct", "Error", "Total Duration", "Score", "AVG Duration", "Input Method"]
            if not os.path.exists(csvFilePath):
                os.makedirs(csvFilePath)
                
            with open(summaryCsvFileName,'w') as f:
                fileCsv = csv.writer(f)
                fileCsv.writerow(headers)
                f.close()
            
        data = [time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), str(right+wrong), str(right), str(wrong), str(duration), str(int((right/(right+wrong))*100)), str(int(duration/(right+wrong))), inputMethod]
        with open(summaryCsvFileName,'a', newline='') as f:
            fileCsv = csv.writer(f)
            fileCsv.writerow(data)
            f.close()
    
    except Exception as e:
        print(e)


def saveToDetailCsv(time, question, answer, input, status, duration, remark):
    """Function to save detail data to CSV file

    |  Args
    |  1. time: the time stamp record the test time
    |  2. question: the content of question
    |  3. answer: the refence answer
    |  4. input: the content from user input
    |  5. status: Pass or Fail
    |  5. duration: only record the time after the app tell the question
    |  7. remark: other information, such as inupt method

    |  Return -- NA

    >>> # save the detail data to CSV file
    >>> saveToDetailCsv(question, answer, input, status, duration, remark)
    """
    try:
        if not os.path.exists(detailCsvFileName):            
            headers = ["Time", "Question", "Answer", "Input", "Status", "Duration", "Remark"]
            if not os.path.exists(csvFilePath):
                os.makedirs(csvFilePath)
                
            with open(detailCsvFileName,'w') as f:
                fileCsv = csv.writer(f)
                fileCsv.writerow(headers)
                f.close()
        
        data = [time, question, answer, input, status, duration, remark]
        with open(detailCsvFileName,'a', newline='') as f:
            fileCsv = csv.writer(f)
            fileCsv.writerow(data)
            f.close()
    
    except Exception as e:
        print(e)

def convertDetailCsvFormat():
    """Function to convert the detail.csv file format

    |  Args -- NA

    |  Return -- NA

    >>> # convert the csv file format
    >>> convertDetailCsvFormat
    """
    csvList = pd.read_csv('detail_old.csv', usecols=['Time', 'op1', 'op', 'op2', 'Answer', 'Input', 'Status', 'Duration', 'Remark'], skiprows=[1, 2])
    print(csvList)
    for i in range(len(csvList)):
        data = [csvList['Time'][i], str(csvList['op1'][i])+str(csvList['op'][i])+str(csvList['op2'][i]), csvList['Answer'][i], csvList['Input'][i], csvList['Status'][i], csvList['Duration'][i], csvList['Remark'][i]]
        print(data)
        saveToDetailCsv(csvList['Time'][i], str(csvList['op1'][i])+str(csvList['op'][i])+str(csvList['op2'][i]), csvList['Answer'][i], csvList['Input'][i], csvList['Status'][i], csvList['Duration'][i], csvList['Remark'][i])

def promptSound(inputMethod=inputKeypad):
    """Function to play the prompt sound

    |  Args:
    |  1. inputMehtod: support Keypad/Speech, will support more in future

    |  Return -- NA

    >>> # play the prompt sound
    >>> promptSound(inputMethod=inputKeypad)
    """
    try:
        if inputMethod==inputKeypad:
            strPrompt = "算术测试开始，请在键盘上输入正确的答案。"
        elif inputMethod==inputSpeech:
            strPrompt = "算术测试开始，请靠近麦克风讲出正确的答案。"
    
        spk = win32com.client.Dispatch("SAPI.SpVoice")
        spk.Speak(strPrompt)
    except Exception as e:
        print(e)

if __name__ == "__main__":
    startTest(inputMethod=inputKeypad)
    #startTest(inputMethod=inputSpeech)
    #convertDetailCsvFormat()
